package com.imooc.points.service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import com.google.common.collect.Lists;
import com.imooc.commons.constant.RedisKeyConstant;
import com.imooc.commons.model.pojo.DinerPoints;
import com.imooc.commons.model.vo.DinerPointsRankVO;
import com.imooc.commons.utils.AssertUtil;
import com.imooc.points.mapper.DinerPointsMapper;

/**
 * \* @author: bear
 * \* Date: 2021-08-17-10:53
 * \* describe:
 */
@Service
public class DinerPointsService {

	@Resource
	private DinerPointsMapper dinerPointsMapper;
	@Resource
	private RestTemplate restTemplate;
	@Resource
	private RedisTemplate redisTemplate;
	@Value("${service.name.ms-oauth-server}")
	private String oauthServerName;
	// 排行榜 TOPN
	private static final int TOPN = 20;

	/**
	 * 添加积分
	 * @param   dinerId 食客id
	 * @param points  积分
	 * @param types 类型 0=签到，1=关注好友，2=添加Feed，3=添加商户评论
	 */
	@Transactional(rollbackFor = Exception.class)
	public void addPoints(Integer dinerId,Integer points,Integer types){
		// 基本参数校验
		AssertUtil.isTrue(dinerId == null || dinerId <1 ,"食客不能为空");
		AssertUtil.isTrue(points == null || points <1,"积分不能为空");
		AssertUtil.isTrue(types == null,"请选择相应的积分类型");


		// 插入数据库
		DinerPoints dinerPoints = new DinerPoints();
		dinerPoints.setFkDinerId(dinerId);
		dinerPoints.setPoints(points);
		dinerPoints.setTypes(types);
		dinerPointsMapper.save(dinerPoints);

		//将积分存入redis
		redisTemplate.opsForZSet().incrementScore(
				RedisKeyConstant.diner_points.getKey(),dinerId,points);
	}


	/**
	 * 查询前 20 积分排行榜，并显示个人排名 -- MySQL
	 *
	 * @param dinerId
	 * @return
	 */
	public List<DinerPointsRankVO> findDinerPointRank(Integer dinerId) {

		//获取前20的排行榜
		List<DinerPointsRankVO> topN = dinerPointsMapper.findTopN(TOPN);
		if(topN == null || topN.isEmpty()){
			return Lists.newArrayList();
		}
		// 根据 key：食客 ID value：积分信息 构建一个 Map
		Map<Integer,DinerPointsRankVO> rankVOMap = new LinkedHashMap<>();
		for (int i = 0; i < topN.size(); i++) {
			rankVOMap.put(topN.get(i).getId(),topN.get(i));
		}
		//判断个人是否在topN中，如果在，添加标记直接返回
		if(rankVOMap.containsKey(dinerId)){
			DinerPointsRankVO myRank = rankVOMap.get(dinerId);
			myRank.setIsMe(1);
			return Lists.newArrayList(rankVOMap.values());
		}
		// 如果不在 ranks 中，获取个人排名追加在最后
		DinerPointsRankVO myRank = dinerPointsMapper.findDinerRank(dinerId);
		myRank.setIsMe(1);
		topN.add(myRank);
		return topN;
	}

	public List<DinerPointsRankVO> findDinerPointRankredis(Integer dinerId) {
		Set<ZSetOperations.TypedTuple<Integer>> rangeWithScores = redisTemplate.opsForZSet()
				.reverseRangeWithScores(RedisKeyConstant.diner_points.getKey(), 0, 19);

		// 初始化食客 ID 集合
		List<Integer> rankDinerIds = Lists.newArrayList();
		// 根据 key：食客 ID value：积分信息 构建一个 Map
		Map<Integer, DinerPointsRankVO> ranksMap = new LinkedHashMap<>();
		// 初始化排名
		int rank =1;
		// 循环处理，添加排行榜信息

		for (ZSetOperations.TypedTuple<Integer> rangeWithScore : rangeWithScores) {
			// 食客ID
			Integer userId = rangeWithScore.getValue();
			// 积分
			int points = rangeWithScore.getScore().intValue();
			// 将食客 ID 添加至食客 ID 集合
			rankDinerIds.add(userId);
			DinerPointsRankVO dinerPointsRankVO = new DinerPointsRankVO();
			dinerPointsRankVO.setId(userId);
			dinerPointsRankVO.setRanks(rank);
			dinerPointsRankVO.setTotal(points);
			// 将 VO 对象添加至 Map 中
			ranksMap.put(dinerId, dinerPointsRankVO);
			// 排名 +1
			rank++;
		}
		// 判断个人是否在 ranks 中，如果在，添加标记直接返回
		if(ranksMap.containsKey(dinerId)){
			DinerPointsRankVO rankVO = new DinerPointsRankVO();
			rankVO.setIsMe(1);
			return Lists.newArrayList(ranksMap.values());
		}

		//如果不在rank中，获取个人排名追加在最后
		Long myRank = redisTemplate.opsForZSet().reverseRank(RedisKeyConstant.diner_points.getKey(), dinerId);
		if(myRank !=null){
			DinerPointsRankVO me = new DinerPointsRankVO();
			me.setRanks(myRank.intValue() +1) ; //排名从0开始
			me.setIsMe(1);
			//获取积分
			Double score = redisTemplate.opsForZSet().score(RedisKeyConstant.diner_points.getKey(), dinerId);
			me.setTotal(score.intValue());
			ranksMap.put(dinerId,me);
		}
		return Lists.newArrayList(ranksMap.values());
	}
}
